using System;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using ProtoScratch.Common;

namespace ProtoScratch.Client
{
	class ClientConnection : ScratchProtocol
	{
		private bool initialized;
		
		/// <summary>
		/// Initializes a new instance of the <see cref="ProtoScratch.Client.ClientConnection"/> class.
		/// </summary>
		internal ClientConnection (ScratchSession session, TcpClient Client) : base (session, Client.GetStream())
		{
			Run ("ProtoScratch Client");
		}
		
		/// <summary>
		/// Handles the client connection.
		/// </summary>
		protected override void HandleSession()
		{
			try
			{
				while (!aborted)
				{
					ProcessMessage();
				}
			}
            catch (Exception e) { Console.WriteLine(e.Message); }
			finally
			{
				handler.ConnectionLost();
			}
		}
		
		/// <summary>
		/// Receive the next frame and pass it to the ScratchEvents handler.
		/// </summary>
		private void ProcessMessage()
		{
			try
			{
				var Message = Receive();
				if (Message.getFlag(ScratchFlags.Internal))
				{
					switch (Message.PrimaryCommand)
					{
					case Internal.Config:
						if (initialized) Abort (); //Don't accept a new initialization... something went wrong.
						ProcessConfigMessage(Message);
						ConfigConfirm();
						handler.InitializeConnection(Send, Abort);
						initialized = true;
						break;
						
					case Internal.PingPong:
						if (Message.SecondaryCommand == Internal.Ping)
							Pong();
						break;
					}
				}
				else
					handler.MessageReceived(Message);
			}
			catch (ScratchException ex)
			{
				if (ex.Type == ScratchException.eType.Timeout)
					Ping();
				else
					throw;
			}
		}
							
		private void ProcessConfigMessage(ScratchMessage Message)
		{
			Int16 l = 0;
			byte[] Data;
			sessionId = Message.SessionID;
			
			if (Message.get (ref l) && Message.get (out Data, l))
				encryptor.encKey = encryptor.RSADecrypt(Data);
			if (Message.get (ref l) && Message.get (out Data, l))
				encryptor.encIV = encryptor.RSADecrypt(Data);
		}
		
		private void ConfigConfirm()
		{
			var Message = new ScratchMessage(0, Internal.Config, Internal.Confirm);
			Message.setFlag(ScratchFlags.Internal, true);
			Send (Message);
		}
	}
}

